\section{Способы передачи аргументов при вызове функций}
\label{sec:callingconventions}

\subsection{cdecl}
\myindex{cdecl}
\label{cdecl}

Этот способ передачи аргументов через стек чаще всего используется в языках \CCpp.

Вызывающая функция заталкивает в стек аргументы в обратном порядке: сначала последний аргумент в стек, 
затем предпоследний, и в самом конце --- первый аргумент. 
Вызывающая функция должна также затем вернуть \glslink{stack pointer}{указатель стека} в нормальное состояние, 
после возврата вызываемой функции.

\begin{lstlisting}[caption=cdecl,style=customasmx86]
push arg3
push arg2
push arg1
call function
add esp, 12 ; returns ESP
\end{lstlisting}

\subsection{stdcall}
\label{sec:stdcall}
\myindex{stdcall}

\newcommand{\SIZEOFINT}{Размер переменной типа \Tint --- 4 в x86-системах и 8 в x64-системах}

Это почти то же что и \IT{cdecl}, за исключением того, что вызываемая функция сама возвращает \ESP 
в нормальное состояние, выполнив инструкцию \TT{RET x} вместо \RET, \\
где \TT{x = количество\_аргументов * sizeof(int)\footnote{\SIZEOFINT}}.
Вызывающая функция не будет корректировать \glslink{stack pointer}{указатель стека},
там нет инструкции \TT{add esp, x}.

\begin{lstlisting}[caption=stdcall,style=customasmx86]
push arg3
push arg2
push arg1
call function

function:
... do something ...
ret 12
\end{lstlisting}

Этот способ используется почти везде в системных библиотеках win32, 
но не в win64 (о win64 смотрите ниже).

\par Например, мы можем взять функцию из 
\myref{src:passing_arguments_ex} и изменить её немного добавив модификатор \TT{\_\_stdcall}:

\begin{lstlisting}[style=customc]
int __stdcall f2 (int a, int b, int c)
{
	return a*b+c;
};
\end{lstlisting}

Он будет скомпилирован почти так же как и \myref{src:passing_arguments_ex_MSVC_cdecl},
но вы увидите \TT{RET 12} вместо \TT{RET}. 
\ac{SP} не будет корректироваться в \glslink{caller}{вызывающей функции}.

Как следствие, количество аргументов функции легко узнать из инструкции \TT{RETN n} просто разделите
$n$ на 4.

\lstinputlisting[caption=MSVC 2010,style=customasmx86]{OS/calling_conventions/stdcall_ex.asm}

\subsubsection{Функции с переменным количеством аргументов}

Функции вроде \printf, должно быть, единственный случай функций в \CCpp с переменным количеством аргументов,
но с их помощью можно легко проследить очень важную разницу между \IT{cdecl} и \IT{stdcall}.
Начнем с того, что компилятор знает сколько аргументов было у \printf.

Однако, вызываемая функция \printf, которая уже давно скомпилирована 
и находится в системной библиотеке MSVCRT.DLL (если говорить о Windows), 
не знает сколько аргументов ей передали, хотя может установить их количество по строке формата.

Таким образом, если бы \printf была \IT{stdcall}-функцией и возвращала \glslink{stack pointer}{указатель стека} в первоначальное состояние 
подсчитав количество аргументов в строке формата, это была бы потенциально опасная ситуация, 
когда одна опечатка программиста могла бы вызывать неожиданные падения программы. 
Таким образом, для таких функций \IT{stdcall} явно не подходит, а подходит \IT{cdecl}.

\subsection{fastcall}
\label{fastcall}
\myindex{fastcall}

Это общее название для передачи некоторых аргументов через регистры, а всех остальных --- через стек.
На более старых процессорах, это работало потенциально быстрее чем \IT{cdecl}/\IT{stdcall} (ведь стек в памяти использовался меньше).
Впрочем, на современных (намного более сложных) CPU, существенного выигрыша может и не быть.

Это не стандартизированный способ, поэтому разные компиляторы делают это по-своему. 
Разумеется, если у вас есть, скажем, две DLL, одна использует другую, и обе они собраны с \IT{fastcall}
но разными компиляторами, очень вероятно, будут проблемы.

MSVC и GCC передает первый и второй аргумент через \ECX и \EDX а остальные аргументы через стек.

\glslink{stack pointer}{Указатель стека} должен быть возвращен в первоначальное состояние вызываемой функцией, 
как в случае \IT{stdcall}.

\begin{lstlisting}[caption=fastcall,style=customasmx86]
push arg3
mov edx, arg2
mov ecx, arg1
call function

function:
.. do something ..
ret 4
\end{lstlisting}

Например, мы можем взять функцию из 
\myref{src:passing_arguments_ex} и изменить её немного добавив модификатор \TT{\_\_fastcall}:

\begin{lstlisting}[style=customc]
int __fastcall f3 (int a, int b, int c)
{
	return a*b+c;
};
\end{lstlisting}

Вот как он будет скомпилирован:

\lstinputlisting[caption=\Optimizing MSVC 2010 /Ob0,style=customasmx86]{OS/calling_conventions/fastcall_ex.asm}

Видно, что \glslink{callee}{вызываемая функция} сама возвращает
 \ac{SP} 
при помощи инструкции \TT{RETN} с операндом.
Так что и здесь можно легко вычислять количество аргументов.

\subsubsection{GCC regparm}

\newcommand{\URLREGPARMM}{\url{http://go.yurichev.com/17040}}

Это в некотором роде, развитие \IT{fastcall}\footnote{\URLREGPARMM}. 
Опцией \TT{-mregparm=x} можно указывать, 
сколько аргументов компилятор будет передавать через регистры. Максимально 3. 
В этом случае будут задействованы регистры \EAX, \EDX и \ECX.

Разумеется, если аргументов у функции меньше трех, то будет задействована только часть регистров.

Вызывающая функция возвращает \glslink{stack pointer}{указатель стека} в первоначальное состояние.

Для примера, см. (\myref{regparm}).

\subsubsection{Watcom/OpenWatcom}
\myindex{OpenWatcom}

Здесь это называется \q{register calling convention}.
Первые 4 аргумента передаются через регистры
\EAX, \EDX, \EBX and \ECX.
Все остальные --- через стек.
Эти функции имеют символ подчеркивания, добавленный к концу имени функции, для отличия их от тех,
которые имеют другой способ передачи аргументов.

\subsection{thiscall}
\myindex{thiscall}

В \Cpp, это передача в функцию-метод указателя \ITthis на объект.

В MSVC указатель \ITthis обычно передается в регистре \ECX.

В GCC указатель \ITthis обычно передается как самый первый аргумент. 
Таким образом, внутри будет видно: у всех функций-методов на один аргумент больше.

Для примера, см. (\myref{thiscall}).

\subsection{x86-64}
\myindex{x86-64}

\subsubsection{Windows x64}
\label{sec:callingconventions_win64}

В win64 метод передачи всех параметров немного похож на \TT{fastcall}. 
Первые 4 аргумента записываются в регистры \RCX, \RDX, \Reg{8}, \Reg{9}, а остальные --- в стек. 
Вызывающая функция также должна подготовить место из 32 байт или для четырех 64-битных значений, 
чтобы вызываемая функция могла сохранить там первые 4 аргумента. 
Короткие функции могут использовать переменные прямо из регистров, 
но б\'{о}льшие могут сохранять их значения на будущее.

Вызывающая функция должна вернуть \glslink{stack pointer}{указатель стека} 
в первоначальное состояние.

Это же соглашение используется и в системных библиотеках Windows x86-64 
(вместо \IT{stdcall} в win32).

Пример:

\lstinputlisting[style=customc]{OS/calling_conventions/x64.c}

\lstinputlisting[caption=MSVC 2012 /0b,style=customasmx86]{OS/calling_conventions/x64_MSVC_Ob.asm}

\myindex{Scratch space}
Здесь мы легко видим, как 7 аргументов передаются: 4 через регистры и остальные 3 через стек.
Код пролога функции f1() сохраняет аргументы в \q{scratch space} --- место в стеке предназначенное
именно для этого.
Это делается потому что компилятор может быть не уверен, достаточно ли ему будет остальных регистров
для работы исключая эти 4, которые иначе будут заняты аргументами до конца исполнения функции.
Выделение \q{scratch space} в стеке лежит на ответственности вызывающей функции.

\lstinputlisting[caption=\Optimizing MSVC 2012 /0b,style=customasmx86]{OS/calling_conventions/x64_MSVC_Ox_Ob.asm}

Если компилировать этот пример с оптимизацией, то выйдет почти то же самое, 
только \q{scratch space} не используется, потому что незачем.

\myindex{x86!\Instructions!LEA}
\label{using_MOV_and_pack_of_LEA_to_load_values}
Обратите также внимание на то как MSVC 2012 оптимизирует примитивную загрузку значений в регистры
используя \LEA (\myref{sec:LEA}).
\INS{MOV} здесь был бы на 1 байт длиннее (5 вместо 4).

Еще один пример подобного: \myref{TaskMgr_LEA}.

\myparagraph{Windows x64: Передача \ITthis (\CCpp)}

Указатель \ITthis передается через \RCX, первый аргумент метода через \RDX, итд.
Для примера, см. также: \myref{simple_CPP_MSVC_x64}.
 
\subsubsection{Linux x64}

Метод передачи аргументов в Linux для x86-64 почти такой же, как и в Windows, но 6 регистров
используется вместо 4 (\RDI, \RSI, \RDX, \RCX, \Reg{8}, \Reg{9}), и здесь нет \q{scratch space}, 
но \gls{callee} может сохранять значения регистров в стеке, если ему это нужно.

\lstinputlisting[caption=\Optimizing GCC 4.7.3,style=customasmx86]{OS/calling_conventions/x64_linux_O3.s}

\myindex{AMD}
N.B.: здесь значения записываются в 32-битные части регистров (например EAX) а не в весь 64-битный
регистр (RAX).
Это связано с тем что в x86-64,
запись в младшую 32-битную часть 64-битного регистра автоматически обнуляет старшие 32 бита.
Должно быть, это так решили в AMD для упрощения портирования кода под x86-64.

\subsection{Возвращение переменных типа \Tfloat, \Tdouble}
\myindex{float}
\myindex{double}

Во всех соглашениях кроме Win64, переменная типа \Tfloat или \Tdouble возвращается через регистр FPU \ST{0}.

В Win64 переменные типа \Tfloat и \Tdouble возвращаются в младших 16-и или 32-х битах 
регистра \XMM{0}.

\subsection{Модификация аргументов}

Иногда программисты на \CCpp{} (и не только этих \ac{PL}) задаются вопросом,
что может случиться, если модифицировать аргументы?

Ответ прост: аргументы хранятся в стеке, именно там и будет происходит модификация.

А вызывающие функции не использует их после вызова функции (автор этих строк никогда не видел в своей практике обратного случая).

\lstinputlisting[style=customc]{OS/calling_conventions/change_arguments.c}

\lstinputlisting[caption=MSVC 2012,style=customasmx86]{OS/calling_conventions/change_arguments.asm}

% TODO (OllyDbg) пример как в стеке меняется $a$

Следовательно, модифицировать аргументы функции можно запросто.
Разумеется, если это не \IT{references} в \Cpp{} (\myref{cpp_references}),
и если вы не модифицируете данные по указателю, 
то эффект не будет распространяться за пределами текущей функции.

Теоретически, после возврата из \gls{callee},
функция-\gls{caller} могла бы получить модифицированный аргумент и использовать его как-то.
Может быть, если бы она была написана на языке ассемблера.

Например, такой код генерирует обычный компилятор \CCpp:

\begin{lstlisting}[style=customasmx86]
	push	456	; will be b
	push	123	; will be a
	call	f	; f() modifies its first argument
	add	esp, 2*4
\end{lstlisting}

Мы можем переписать так:

\begin{lstlisting}[style=customasmx86]
	push	456	; will be b
	push	123	; will be a
	call	f	; f() modifies its first argument
	pop	eax
	add	esp, 4
	; EAX=1st argument of f() modified in f()
\end{lstlisting}

Трудно представить, кому может это понадобиться, но на практике это возможно.
Так или иначе, стандарты языков \CCpp не предлагают никакого способа это сделать.

% sections
\input{OS/calling_conventions/ptr_to_argument/main_RU}

